/*-------------------------------------------------------------------------
 *
 * array.h
 *	  Postgres数组的声明。
 *
 * 标准的变长数组具有以下内部结构：
 *	  <vl_len_>		- 标准变长头字
 *	  <ndim>		- 数组的维数
 *	  <dataoffset>	- 存储数据的偏移，若无空值位图则为0
 *	  <elemtype>	- 元素类型OID
 *	  <dimensions>	- 每个数组轴的长度（int的C数组）
 *	  <lower bnds>	- 每个维度的下界（int的C数组）
 *	  <null bitmap> - 显示空值位置的位图（可选）
 *	  <actual data> - 存储的数据
 *
 * <dimensions>和<lower bnds>数组各有ndim个元素。
 *
 * 如果数组不包含NULL元素，则可以省略<null bitmap>。
 * 如果缺失，<dataoffset>字段为零，存储数据的偏移必须动态计算。
 * 如果位图存在，<dataoffset>为非零，等于从数组开始到第一个数据元素的偏移（包括对齐填充）。
 * 位图遵循与元组NULL位图相同的约定，即，1表示非空条目，且每个位图字节的最低有效位首先使用。
 *
 * 实际数据在MAXALIGN边界开始。数组中的单个项根据数组元素类型的规定进行对齐。
 * 它们按行优先顺序存储（最后一个下标变化最快）。
 *
 * 注意：重要的是可烤焙数据类型的数组元素不得被烤焙，因为元组烤焙器将不知道它们存在。
 * （我们可以支持压缩的烤焙项；只有不在行内的项才是危险的。
 * 不过，把这些项存储为未压缩是可取的，并允许烤焙器将整个数组作为一个输入进行压缩。）
 *
 * OIDVECTOR和INT2VECTOR数据类型与通用数组在存储上兼容，但它们只支持无空值的一维数组（并且没有空值位图）。
 * 它们也不支持被烤焙。
 *
 * 还有一些“定长数组”数据类型，如NAME和POINT。它们只是固定数量项的顺序，每项都是固定长度的数据类型，没有开销；
 * 项大小必须是其对齐要求的倍数，因为我们不进行填充。
 * 我们支持对这些类型的下标，但是array_in()和array_out()仅适用于变长数组。
 *
 * 此外，数组是“扩展对象”TOAST基础设施的主要用户。这允许将变长数组转换为可能包含“解构”Datum/isnull数组的单独表示。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/utils/array.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef ARRAY_H
#define ARRAY_H

#include "fmgr.h"
#include "utils/expandeddatum.h"

/* 避免在此处包含 execnodes.h */
struct ExprState;
struct ExprContext;


/*
 * 数组下标的最大数量（任意限制）
 */
#define MAXDIM 6

/*
 * 数组中元素的最大数量。我们将此限制为最多约两亿五千万个元素，以便无需在如此多地方检查溢出——例如在 palloc'ing Datum 数组时。
 */
#define MaxArraySize ((Size) (MaxAllocSize / sizeof(Datum)))

/*
 * 数组是 varlena 对象，因此必须符合 varlena 约定，即对象的第一个 int32 包含对象的总大小（以字节为单位）。
 * 但是，请确保使用 VARSIZE() 和 SET_VARSIZE() 来访问它！
 *
 * 注意：如果修改普通数组的头部，还需要修改 oidvector 和 int2vector 的头部！
 */
typedef struct ArrayType
{
	int32		vl_len_;		/* varlena 头部（请勿直接触摸！） */
	int			ndim;			/* 维度数量 */
	int32		dataoffset;		/* 数据的偏移量，如果没有位图则为 0 */
	Oid			elemtype;		/* 元素类型 OID */
} ArrayType;

/*
 * 扩展数组包含在一个私有内存上下文中（所有扩展对象都必须如此），并具有如下控制结构。
 *
 * 如果扩展数组包含一个常规的“扁平”数组（如果这是原始输入并且我们尚未对其进行重大修改），则扩展数组可能包含该数组。否则，内容由 Datum/isnull 数组加上维度和类型信息表示。如果我们为了访问目的解构了原始数组但尚未更改它，则也可以同时包含这两种形式。在通过引用传递的元素类型中，在这种情况下，Datums 将指向扁平数组。一旦我们开始修改数组元素，新的通过引用传递的元素将在内存上下文中单独进行 palloc。
 */
#define EA_MAGIC 689375833		/* 用于调试交叉检查的 ID */

typedef struct ExpandedArrayHeader
{
	/* 扩展对象的标准头部 */
	ExpandedObjectHeader hdr;

	/* 标识扩展数组的魔法值（仅用于调试） */
	int			ea_magic;

	/* 维度信息（始终有效） */
	int			ndims;			/* 维度数量 */
	int		   *dims;			/* 数组维度 */
	int		   *lbound;			/* 每个维度的索引下界 */

	/* 元素类型信息（始终有效） */
	Oid			element_type;	/* 元素类型 OID */
	int16		typlen;			/* 关于元素数据类型的必要信息 */
	bool		typbyval;
	char		typalign;

	/*
	 * 如果我们有数组的 Datum-array 表示，它将保存在这里；
	 * 否则 dvalues/dnulls 将为 NULL。dvalues 和 dnulls 数组始终在对象私有上下文中进行 palloc，但可能会不时更改大小。对于通过引用传递的元素类型，dvalues 条目可能指向 fstartptr..fendptr 区域，或指向单独 palloc 的块。元素应始终完全去烘烤，因为它们在标准扁平表示中。
	 *
	 * 即使 dvalues 是有效的，如果没有空元素，dnulls 也可以为 NULL。
	 */
	Datum	   *dvalues;		/* Datum 数组 */
	bool	   *dnulls;			/* is-null 标志的数组用于 Datums */
	int			dvalueslen;		/* 上述数组的分配长度 */
	int			nelems;			/* 上述数组中有效条目的数量 */

	/*
	 * flat_size 是目前所需的平面等效扩展数组的空间要求，如果已知；否则为 0。我们存储这个值以使 consecutive 调用 get_flat_size 变得便宜。
	 */
	Size		flat_size;

	/*
	 * fvalue 指向有效的平面表示，否则为 NULL。如果我们有或曾经有平面表示，则 fstartptr/fendptr 指向其数据区域的起始和结束 + 1；这是为了让我们能够判断哪些 Datum 指针指向平面表示，而不是指向单独 palloc 的数据。
	 */
	ArrayType  *fvalue;			/* 必须是一个完全去烤的数组 */
	char	   *fstartptr;		/* 其数据区域的开始 */
	char	   *fendptr;		/* 其数据区域的结束 + 1 */
} ExpandedArrayHeader;

/*
 * 可以处理“平面” varlena 数组或扩展数组的函数使用此联合来处理它们的输入。不要引用“flt”；相反，转换为 ArrayType。这个结构名义上在 64 位上要求 8 字节对齐，但它通常用于具有 4 字节对齐的 ArrayType。在这样的情况下，UBSan 抱怨引用“flt”。
 */
typedef union AnyArrayType
{
	ArrayType	flt;
	ExpandedArrayHeader xpn;
} AnyArrayType;

/*
 * accumArrayResult() 和朋友的工作状态
 * 请注意，输入必须是标量（合法数组元素）
 */
typedef struct ArrayBuildState
{
	MemoryContext mcontext;		/* 所有临时信息都保存在这里 */
	Datum	   *dvalues;		/* 累计的 Datums 数组 */
	bool	   *dnulls;			/* is-null 标志的数组用于 Datums */
	int			alen;			/* 上述数组的分配长度 */
	int			nelems;			/* 上述数组中有效条目的数量 */
	Oid			element_type;	/* Datums 的数据类型 */
	int16		typlen;			/* 关于数据类型的必要信息 */
	bool		typbyval;
	char		typalign;
	bool		private_cxt;	/* 使用私有内存上下文 */
} ArrayBuildState;

/*
 * accumArrayResultArr() 和朋友的工作状态
 * 请注意，输入必须是数组，并且返回相同的数组类型
 */
typedef struct ArrayBuildStateArr
{
	MemoryContext mcontext;		/* 所有临时信息都保存在这里 */
	char	   *data;			/* 累计数据 */
	bits8	   *nullbitmap;		/* is-null 标志的位图，如果没有则为 NULL */
	int			abytes;			/* “data”的分配长度 */
	int			nbytes;			/* 到目前为止使用的字节数 */
	int			aitems;			/* 位图的分配长度（以元素计） */
	int			nitems;			/* 结果中的元素总数 */
	int			ndims;			/* 结果的当前维度 */
	int			dims[MAXDIM];
	int			lbs[MAXDIM];
	Oid			array_type;		/* 数组的数据类型 */
	Oid			element_type;	/* 数组元素的数据类型 */
	bool		private_cxt;	/* 使用私有内存上下文 */
} ArrayBuildStateArr;

/*
 * accumArrayResultAny() 和朋友的工作状态
 * 这些函数处理两种情况
 */
typedef struct ArrayBuildStateAny
{
	/* 正好有一个不是 NULL： */
	ArrayBuildState *scalarstate;
	ArrayBuildStateArr *arraystate;
} ArrayBuildStateAny;

/*
 * 用于缓存数组操作所需类型元数据的结构 
 */
typedef struct ArrayMetaState
{
	Oid			element_type;
	int16		typlen;
	bool		typbyval;
	char		typalign;
	char		typdelim;
	Oid			typioparam;
	Oid			typiofunc;
	FmgrInfo	proc;
} ArrayMetaState;

/*
 * 数组映射所需的私有状态（在此处，因为调用者必须提供它）
 */
typedef struct ArrayMapState
{
	ArrayMetaState inp_extra;
	ArrayMetaState ret_extra;
} ArrayMapState;

/* ArrayIteratorData 在 arrayfuncs.c 中是私有的 */
typedef struct ArrayIteratorData *ArrayIterator;

/* 常规 varlena 数组对象的 fmgr 宏 */
#define DatumGetArrayTypeP(X)		  ((ArrayType *) PG_DETOAST_DATUM(X))
#define DatumGetArrayTypePCopy(X)	  ((ArrayType *) PG_DETOAST_DATUM_COPY(X))
#define PG_GETARG_ARRAYTYPE_P(n)	  DatumGetArrayTypeP(PG_GETARG_DATUM(n))
#define PG_GETARG_ARRAYTYPE_P_COPY(n) DatumGetArrayTypePCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_ARRAYTYPE_P(x)	  PG_RETURN_POINTER(x)

/* 扩展数组对象的 fmgr 宏 */
#define PG_GETARG_EXPANDED_ARRAY(n)  DatumGetExpandedArray(PG_GETARG_DATUM(n))
#define PG_GETARG_EXPANDED_ARRAYX(n, metacache) \
	DatumGetExpandedArrayX(PG_GETARG_DATUM(n), metacache)
#define PG_RETURN_EXPANDED_ARRAY(x)  PG_RETURN_DATUM(EOHPGetRWDatum(&(x)->hdr))

/* AnyArrayType 的 fmgr 宏（即，获取 varlena 或扩展形式） */
#define PG_GETARG_ANY_ARRAY_P(n)	DatumGetAnyArrayP(PG_GETARG_DATUM(n))

/*
 * 访问 varlena 数组头字段的宏。
 *
 * ARR_DIMS 返回指向数组维度的指针（沿各个数组轴的元素数量）。
 *
 * ARR_LBOUND 返回指向数组下界的指针。
 *
 * 也就是说：如果数组的第三个轴的元素是 5 到 8，那么
 * ARR_DIMS(a)[2] == 4 且 ARR_LBOUND(a)[2] == 5。
 *
 * 与 C 不同，默认下界是 1。
 */
#define ARR_SIZE(a)				VARSIZE(a)
#define ARR_NDIM(a)				((a)->ndim)
#define ARR_HASNULL(a)			((a)->dataoffset != 0)
#define ARR_ELEMTYPE(a)			((a)->elemtype)

#define ARR_DIMS(a) \
		((int *) (((char *) (a)) + sizeof(ArrayType)))
#define ARR_LBOUND(a) \
		((int *) (((char *) (a)) + sizeof(ArrayType) + \
				  sizeof(int) * ARR_NDIM(a)))

#define ARR_NULLBITMAP(a) \
		(ARR_HASNULL(a) ? \
		 (bits8 *) (((char *) (a)) + sizeof(ArrayType) + \
					2 * sizeof(int) * ARR_NDIM(a)) \
		 : (bits8 *) NULL)

/*
 * 指定维度数量和总项目数量的数组的总数组头大小（以字节为单位）。
 */
#define ARR_OVERHEAD_NONULLS(ndims) \
		MAXALIGN(sizeof(ArrayType) + 2 * sizeof(int) * (ndims))
#define ARR_OVERHEAD_WITHNULLS(ndims, nitems) \
		MAXALIGN(sizeof(ArrayType) + 2 * sizeof(int) * (ndims) + \
				 ((nitems) + 7) / 8)

#define ARR_DATA_OFFSET(a) \
		(ARR_HASNULL(a) ? (a)->dataoffset : ARR_OVERHEAD_NONULLS(ARR_NDIM(a)))

/*
 * 返回指向实际数组数据的指针。
 */
#define ARR_DATA_PTR(a) \
		(((char *) (a)) + ARR_DATA_OFFSET(a))

/*
 * 处理 AnyArrayType 输入的宏。 注意多个引用！
 */
#define AARR_NDIM(a) \
	(VARATT_IS_EXPANDED_HEADER(a) ? \
	 (a)->xpn.ndims : ARR_NDIM((ArrayType *) (a)))
#define AARR_HASNULL(a) \
	(VARATT_IS_EXPANDED_HEADER(a) ? \
	 ((a)->xpn.dvalues != NULL ? (a)->xpn.dnulls != NULL : ARR_HASNULL((a)->xpn.fvalue)) : \
	 ARR_HASNULL((ArrayType *) (a)))
#define AARR_ELEMTYPE(a) \
	(VARATT_IS_EXPANDED_HEADER(a) ? \
	 (a)->xpn.element_type : ARR_ELEMTYPE((ArrayType *) (a)))
#define AARR_DIMS(a) \
	(VARATT_IS_EXPANDED_HEADER(a) ? \
	 (a)->xpn.dims : ARR_DIMS((ArrayType *) (a)))
#define AARR_LBOUND(a) \
	(VARATT_IS_EXPANDED_HEADER(a) ? \
	 (a)->xpn.lbound : ARR_LBOUND((ArrayType *) (a)))


/*
 * GUC参数
 */
extern PGDLLIMPORT bool Array_nulls;

/*
 * 在 arrayfuncs.c 中定义的函数原型
 */
extern void CopyArrayEls(ArrayType *array,
						 Datum *values,
						 bool *nulls,
						 int nitems,
						 int typlen,
						 bool typbyval,
						 char typalign,
						 bool freedata);

extern Datum array_get_element(Datum arraydatum, int nSubscripts, int *indx,
							   int arraytyplen, int elmlen, bool elmbyval, char elmalign,
							   bool *isNull);
extern Datum array_set_element(Datum arraydatum, int nSubscripts, int *indx,
							   Datum dataValue, bool isNull,
							   int arraytyplen, int elmlen, bool elmbyval, char elmalign);
extern Datum array_get_slice(Datum arraydatum, int nSubscripts,
							 int *upperIndx, int *lowerIndx,
							 bool *upperProvided, bool *lowerProvided,
							 int arraytyplen, int elmlen, bool elmbyval, char elmalign);
extern Datum array_set_slice(Datum arraydatum, int nSubscripts,
							 int *upperIndx, int *lowerIndx,
							 bool *upperProvided, bool *lowerProvided,
							 Datum srcArrayDatum, bool isNull,
							 int arraytyplen, int elmlen, bool elmbyval, char elmalign);

extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
					   int arraytyplen, int elmlen, bool elmbyval, char elmalign,
					   bool *isNull);
extern ArrayType *array_set(ArrayType *array, int nSubscripts, int *indx,
							Datum dataValue, bool isNull,
							int arraytyplen, int elmlen, bool elmbyval, char elmalign);

extern Datum array_map(Datum arrayd,
					   struct ExprState *exprstate, struct ExprContext *econtext,
					   Oid retType, ArrayMapState *amstate);

extern void array_bitmap_copy(bits8 *destbitmap, int destoffset,
							  const bits8 *srcbitmap, int srcoffset,
							  int nitems);

extern ArrayType *construct_array(Datum *elems, int nelems,
								  Oid elmtype,
								  int elmlen, bool elmbyval, char elmalign);
extern ArrayType *construct_md_array(Datum *elems,
									 bool *nulls,
									 int ndims,
									 int *dims,
									 int *lbs,
									 Oid elmtype, int elmlen, bool elmbyval, char elmalign);
extern ArrayType *construct_empty_array(Oid elmtype);
extern ExpandedArrayHeader *construct_empty_expanded_array(Oid element_type,
														   MemoryContext parentcontext,
														   ArrayMetaState *metacache);
extern void deconstruct_array(ArrayType *array,
							  Oid elmtype,
							  int elmlen, bool elmbyval, char elmalign,
							  Datum **elemsp, bool **nullsp, int *nelemsp);
extern bool array_contains_nulls(ArrayType *array);

extern ArrayBuildState *initArrayResult(Oid element_type,
										MemoryContext rcontext, bool subcontext);
extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
										 Datum dvalue, bool disnull,
										 Oid element_type,
										 MemoryContext rcontext);
extern Datum makeArrayResult(ArrayBuildState *astate,
							 MemoryContext rcontext);
extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
							   int *dims, int *lbs, MemoryContext rcontext, bool release);

extern ArrayBuildStateArr *initArrayResultArr(Oid array_type, Oid element_type,
											  MemoryContext rcontext, bool subcontext);
extern ArrayBuildStateArr *accumArrayResultArr(ArrayBuildStateArr *astate,
											   Datum dvalue, bool disnull,
											   Oid array_type,
											   MemoryContext rcontext);
extern Datum makeArrayResultArr(ArrayBuildStateArr *astate,
								MemoryContext rcontext, bool release);

extern ArrayBuildStateAny *initArrayResultAny(Oid input_type,
											  MemoryContext rcontext, bool subcontext);
extern ArrayBuildStateAny *accumArrayResultAny(ArrayBuildStateAny *astate,
											   Datum dvalue, bool disnull,
											   Oid input_type,
											   MemoryContext rcontext);
extern Datum makeArrayResultAny(ArrayBuildStateAny *astate,
								MemoryContext rcontext, bool release);

extern ArrayIterator array_create_iterator(ArrayType *arr, int slice_ndim, ArrayMetaState *mstate);
extern bool array_iterate(ArrayIterator iterator, Datum *value, bool *isnull);
extern void array_free_iterator(ArrayIterator iterator);

/*
 * 在 arrayutils.c 中定义的函数原型
 */

extern int	ArrayGetOffset(int n, const int *dim, const int *lb, const int *indx);
extern int	ArrayGetOffset0(int n, const int *tup, const int *scale);
extern int	ArrayGetNItems(int ndim, const int *dims);
extern void ArrayCheckBounds(int ndim, const int *dims, const int *lb);
extern void mda_get_range(int n, int *span, const int *st, const int *endp);
extern void mda_get_prod(int n, const int *range, int *prod);
extern void mda_get_offset_values(int n, int *dist, const int *prod, const int *span);
extern int	mda_next_tuple(int n, int *curr, const int *span);
extern int32 *ArrayGetIntegerTypmods(ArrayType *arr, int *n);

/*
 * 在 array_expanded.c 中定义的函数原型
 */
extern Datum expand_array(Datum arraydatum, MemoryContext parentcontext,
						  ArrayMetaState *metacache);
extern ExpandedArrayHeader *DatumGetExpandedArray(Datum d);
extern ExpandedArrayHeader *DatumGetExpandedArrayX(Datum d,
												   ArrayMetaState *metacache);
extern AnyArrayType *DatumGetAnyArrayP(Datum d);
extern void deconstruct_expanded_array(ExpandedArrayHeader *eah);

#endif							/* ARRAY_H */
